﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace WIAssistant
{
    public static class WorkItemHelper
    {
        public static bool IsOpenInAnyWay(this WorkItem workItem)
        {
            return (workItem.IsOpen || workItem.IsPartialOpen || workItem.IsReadOnlyOpen);
        }

        /// <summary>
        /// Try to open the work item.  If an except is thrown then catch and suppress it.
        /// </summary>
        /// <param name="workItem"></param>
        public static void TryOpen(this WorkItem workItem)
        {
            try
            {
                workItem.Open();
            }
            catch (Exception)
            {
                
            }
        }

        // Not Used
        // Copied from the Hemi Project.  (It has a "free for any user" license so that is ok.)
        public static void AddState(this WorkItemType workItemType, string newState)
        {
            XmlDocument xml = workItemType.Export(false);


            // Do we have a 'Moved' state?
            XmlNodeList statesList = xml.GetElementsByTagName("STATES");


            // There should only be 1
            XmlNode states = statesList[0];

            bool hasMoved = false;
            List<string> otherStates = new List<string>();
            XmlNode movedState = null;

            foreach (XmlNode state in states)
            {
                string stateValue = state.Attributes["value"].Value;
                otherStates.Add(stateValue);

                if (stateValue.Equals(newState))
                {
                    hasMoved = true;
                }

                if (movedState == null)
                {
                    // We might need to create a moved state, so clone the
                    // node now since we have easy access to other state nodes
                    movedState = state.CloneNode(false);
                }
            }

            if ((!hasMoved) && (movedState != null))
            {
                // Add this state, as well as transitions
                movedState.Attributes["value"].Value = newState;
                states.AppendChild(movedState);

                // Add in the necessary transitions
                XmlNodeList transitionsList = xml.GetElementsByTagName("TRANSITIONS");

                // There should only be 1
                XmlNode transitions = transitionsList[0];

                // Add a transistion so that all other states can go to this new state.
                foreach (string otherState in otherStates)
                {
                    XmlNode newTransition = transitions.ChildNodes[0].CloneNode(false);
                    newTransition.Attributes["from"].Value = otherState;
                    newTransition.Attributes["to"].Value = newState;

                    XmlNode reasons = xml.CreateNode(XmlNodeType.Element, "REASONS", newTransition.NamespaceURI);

                    XmlNode defaultReason = xml.CreateNode(XmlNodeType.Element, "DEFAULTREASON", reasons.NamespaceURI);
                    XmlAttribute reasonAttribute = xml.CreateAttribute("value");
                    reasonAttribute.Value = "This work item has been " + newState + ".";
                    defaultReason.Attributes.Append(reasonAttribute);

                    reasons.AppendChild(defaultReason);

                    newTransition.AppendChild(reasons);

                    transitions.AppendChild(newTransition);
                }

                workItemType.Project.WorkItemTypes.Import((XmlElement)xml.FirstChild);
                workItemType.Store.RefreshCache();
                workItemType.Store.SyncToCache();
            }
        }

        // Not Used
        // Hold a list of all the transistions we have done.  This will help us not have run them again If we already have.
        private static readonly Dictionary<WorkItemType, List<Transition>> _allTransistions = new Dictionary<WorkItemType, List<Transition>>();

        /// <summary>
        /// Deprecated
        /// Get the transitions for this <see cref="WorkItemType"/>
        /// </summary>
        /// <param name="workItemType"></param>
        /// <returns></returns>
        public static List<Transition> GetTransitions(this WorkItemType workItemType)
        {
            List<Transition> currentTransistions;

            // See if this WorkItemType has already had it's transistions figured out.
            _allTransistions.TryGetValue(workItemType, out currentTransistions);
            if (currentTransistions != null)
                return currentTransistions;

            // Get this worktype type as xml
            XmlDocument workItemTypeXml = workItemType.Export(false);

            // Create a dictionary to allow us to look up the "to" state using a "from" state.
            var newTransistions = new List<Transition>();

            // get the transistions node.
            XmlNodeList transitionsList = workItemTypeXml.GetElementsByTagName("TRANSITIONS");

            // As there is only one transistions item we can just get the first
            XmlNode transitions = transitionsList[0];

            // Iterate all the transitions
            foreach (XmlNode transitionXML in transitions)
            {
                // See if we have this from state already.
                string fromState = transitionXML.Attributes["from"].Value;
                Transition transition = newTransistions.Find(trans => trans.From == fromState);
                if (transition != null)
                {
                    transition.To.Add(transitionXML.Attributes["to"].Value);
                }
                // If we could not find this state already then add it.
                else
                {
                    // save off the transistion (from first so we can look up state progression.
                    newTransistions.Add(new Transition
                    {
                        From = transitionXML.Attributes["from"].Value,
                        To = new List<string>{transitionXML.Attributes["to"].Value}
                    });    
                }
            }

            // Save off this transition so we don't do it again if it is needed.
            _allTransistions.Add(workItemType, newTransistions);

            return newTransistions;
        }


        public static IEnumerable<string> FindNextStateHopper(this WorkItemType wiType, string startState, string finalState)
        {
            Dictionary<string, string> map = new Dictionary<string, string>();
            Dictionary<string, List<string>> edges = wiType.GetTransitions().ToDictionary(i => i.From, i => i.To);
            Queue<string> q = new Queue<string>();
            map.Add(startState, null);
            q.Enqueue(startState);
            while (q.Count > 0)
            {
                string current = q.Dequeue();
                foreach (string s in edges[current])
                {
                    if (!map.ContainsKey(s))
                    {
                        q.Enqueue(s);
                        map.Add(s, current);
                    }
                }
            }
            if (!map.ContainsKey(finalState)) // no path exists
                yield break;
            var result = new Stack<string>();
            var thisNode = finalState;
            do
            {
                result.Push(thisNode);
                thisNode = map[thisNode];
            } while (thisNode != startState);
            while (result.Count > 0)
                yield return result.Pop();
        }

        public static IEnumerable<string> FindNextState(this WorkItemType wiType, string fromState, string toState)
        {
            var map = new Dictionary<string, string>();
            var edges = wiType.GetTransitions().ToDictionary(i => i.From, i => i.To);
            var q = new Queue<string>();
            map.Add(fromState, null);
            q.Enqueue(fromState);
            while (q.Count > 0)
            {
                var current = q.Dequeue();
                foreach (var s in edges[current])
                {
                    if (!map.ContainsKey(s))
                    {
                        map.Add(s, current);
                        if (s == toState)
                        {
                            var result = new Stack<string>();
                            var thisNode = s;
                            do
                            {
                                result.Push(thisNode);
                                thisNode = map[thisNode];
                            } while (thisNode != fromState);
                            while (result.Count > 0)
                                yield return result.Pop();
                            yield break;
                        }
                        q.Enqueue(s);
                    }
                }
            }
            // no path exists
        }


        public static FieldDefinition GetFieldDefinition(this WorkItemType workItemType, string fieldName)
        {
            foreach (FieldDefinition definition in workItemType.FieldDefinitions)
            {
                if (definition.Name == fieldName)
                    return definition;
            }
            return null;
        }

        public static WITSelectorNode GetWITDefaultFieldFromMemory(this WorkItemType wiType)
        {
            WITSelectorNode savedNode = null;
            foreach (WITSelectorProject selectorProject in MainMediator.Instance.WITSelectorProjects)
            {
                if (selectorProject.WITProjectName == wiType.Project.Name)
                {
                    foreach (WITSelectorNode selectorNode in selectorProject.WITNodes)
                    {
                        if (selectorNode.WorkItemTypeName == wiType.Name)
                        {
                            savedNode = selectorNode;
                            break;
                        }
                    }
                    break;
                }
            }
            return savedNode;
        }

        public static object GetDefaultColumnValue(this WorkItem workItem)
        {
            var node = workItem.Type.GetWITDefaultFieldFromMemory();
            string defaultColumn;
            if (node != null)
            {
                defaultColumn = node.DefaultFieldName;
            }
            else
            {
                defaultColumn = "Title";
            }
            return workItem.Fields[defaultColumn].Value;
        }
    }

}
